/*
 * Copyright (c) 2009-2013 EvTech Project
 * All rights reserved.
 * 
 * This file is part of LModeller.
 *
 * LModeller is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version.
 *
 * LModeller is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LModeller.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package fi.honkalampisaatio.lmodeller;

import java.awt.SplashScreen;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;

import javax.xml.bind.JAXBException;

import org.lwjgl.opengl.*;
import org.lwjgl.input.*;
import org.lwjgl.util.glu.*;

import glapp.*;
import fi.honkalampisaatio.game.xml.KeyValuePair;
import fi.honkalampisaatio.game.xml.LanguageSettings;
import fi.honkalampisaatio.game.xml.SettingsReader;
import fi.honkalampisaatio.lmodeller.LanguageResource;
import fi.honkalampisaatio.lmodeller.blocks.BlockBox;
import fi.honkalampisaatio.lmodeller.selection.*;
import fi.honkalampisaatio.lmodeller.selection.GameModeSelectionEvent.MODES;
import fi.honkalampisaatio.lmodeller.sound.BackgroundMusic;
import fi.honkalampisaatio.lmodeller.ui.*;
import fi.honkalampisaatio.lmodeller.ui.Model3DView.VIEWMODES;
import fi.honkalampisaatio.lmodeller.xml.GLSettings;
import fi.honkalampisaatio.lmodeller.xml.GameSettings;
import fi.honkalampisaatio.sound.VoicePlayer;

public class LModeller extends GLApp implements SelectionListener {
    // Light direction: if last value is 0, then this describes light direction.  If 1, then light position.
    float lightDirection[]= { -2f, 2f, 2f, 0f };

	public enum VIEWS {USER_MENU, GAME_MENU, RANDOM_MENU, MODEL_MENU, VIEW_MODEL, SETUP_MENU, BUILD_MENU};
	private VIEWS view = VIEWS.USER_MENU;

	public final static String FONTNAME = "fontName";

	public final static String SETTINGFILE = "./data/settings.xml";
	public static final String LANGUAGEFOLDER = "./data/languages/";
	
	public static float actionColor[] = {1.0f, 0.89f, 0.631f, 1f};
	public static float directionColor[] = {0.424f, 0.878f, 0.851f, 1f};
	public static float testColor[] = {0.5f, 0.5f, 0.5f, 1f};
	
	static GameSettings gameSettings;
	ArrayList<VIEWS> viewHistory;
	
	UserMenu userMenu;
	GameMenu gameMenu;
	BuildMenu buildMenu;
	SetupMenu setupMenu;
	RandomMenu randomMenu;
	ModelMenu modelMenu;
	ViewModel viewModel;
    
	LModeller()
	{
		Logger.getInstance().setDirectoryPath("./logs/");
    	loadSettings();
		viewHistory = new ArrayList<VIEWS>();

    	if(gameSettings!=null && gameSettings.getGlSettings()!=null && gameSettings.getGlSettings().getFullScreen())
    	{
    		userMenu = new UserMenu(gameSettings.getShowExit());
    	}
    	else
    	{
    		userMenu = new UserMenu(false);
    	}
		userMenu.addSelectionListener(this);
		gameMenu = new GameMenu();
		gameMenu.addSelectionListener(this);
		buildMenu = new BuildMenu();
		buildMenu.addSelectionListener(this);
		setupMenu = new SetupMenu();
		setupMenu.addSelectionListener(this);
		modelMenu = new ModelMenu();
		modelMenu.addSelectionListener(this);
		viewModel = new ViewModel();
		viewModel.addSelectionListener(this);

		if(gameSettings!=null || gameSettings.getRandomMenuType()!=null)
		{
			if(gameSettings.getRandomMenuType() == GameSettings.RANDOMMENU_TYPE.TABLE)
			{
				randomMenu = new RandomNumberTableMenu(gameSettings.getMinBlockCountInRandomMenu(), 
						gameSettings.getMaxBlockCountInRandomMenu(),
						gameSettings.getColumnCountInRandomMenu());
			}
			else
			{
				randomMenu = new RandomSpinnerMenu(gameSettings.getMinBlockCountInRandomMenu(), 
						gameSettings.getMaxBlockCountInRandomMenu());
			}
		}
		else
		{
			randomMenu = new RandomSpinnerMenu(3, 100);
		}
		randomMenu.addSelectionListener(this);
    	if(gameSettings!=null)
    	{
    		BlockBox.getBlockBox().loadBlocks(gameSettings.getBlocks());
        	GLSettings gls = gameSettings.getGlSettings();
        	if(gameSettings.getActionColor()!=null && gameSettings.getActionColor().startsWith("#") &&
        			gameSettings.getActionColor().length()==7)
        	{
        		try
        		{
            		actionColor[0] = Integer.parseInt(gameSettings.getActionColor().substring(1, 3), 16)/255f;
            		actionColor[1] = Integer.parseInt(gameSettings.getActionColor().substring(3, 5), 16)/255f;
            		actionColor[2] = Integer.parseInt(gameSettings.getActionColor().substring(5, 7), 16)/255f;
        		}
        		catch(Exception e)
        		{
            		actionColor[0] = 1.0f;
            		actionColor[1] = 0.89f;
            		actionColor[2] = 0.631f;
            		System.err.println("Can't convert " + gameSettings.getActionColor() + ", using color #FFE3A1 for actionColor");
        		}
        	}
        	if(gameSettings.getDirectionColor()!=null && gameSettings.getDirectionColor().startsWith("#") &&
        			gameSettings.getDirectionColor().length()==7)
        	{
        		try
        		{
        			directionColor[0] = Integer.parseInt(gameSettings.getDirectionColor().substring(1, 3), 16)/255f;
        			directionColor[1] = Integer.parseInt(gameSettings.getDirectionColor().substring(3, 5), 16)/255f;
        			directionColor[2] = Integer.parseInt(gameSettings.getDirectionColor().substring(5, 7), 16)/255f;
        		}
        		catch(Exception e)
        		{
        			directionColor[0] = 0.424f;
        			directionColor[1] = 0.878f;
        			directionColor[2] = 0.851f;
            		System.err.println("Can't convert " + gameSettings.getDirectionColor() + ", using color #6CE0D9 for directionColor");
        		}
        	}
    		if(gls!=null)
    		{
	    		GLApp.fullScreen = gls.getFullScreen();
    			if(gls.getDisplayHeight()!=0)
    			{
    				GLApp.displayHeight = gls.getDisplayHeight();
    			}
    			if(gls.getDisplayWidth()!=0)
    			{
    				GLApp.displayWidth = gls.getDisplayWidth();
    			}
    		}
    		File folder = new File(LANGUAGEFOLDER);
    		if (folder.exists()) {
    		    File[] listOfFiles = folder.listFiles();
    		    for (int i = 0; i < listOfFiles.length; i++) {
    			      if (listOfFiles[i].isFile()) {
    			    	  String filename = listOfFiles[i].getName();
    			    	  String ext = filename.substring(filename.lastIndexOf('.')+1,filename.length());
    			    	if (ext.equalsIgnoreCase("xml") ) {  
    			    		LanguageSettings ls = loadLanguageFile(listOfFiles[i].getPath());
    			    		if(ls!=null && ls.getLanguage().equalsIgnoreCase(gameSettings.getLanguage()))
    			    		{
    			    			Iterator<KeyValuePair> it = ls.getValueList().iterator();
    			    			while(it.hasNext()) {
    			    				KeyValuePair value = it.next();
    			    				LanguageResource lr = LanguageResource.getInstance();
    			    				lr.setValue(value.getKey(), value.getValue());
    			    			}
    			    		}
    			      	}
    			      }
    			    }
    		}
			GLApp.showMessages = false;
			VoicePlayer.getInstance().setOn(false);
    	}
	}

	private LanguageSettings loadLanguageFile(String languageFile) {
		if(languageFile==null)
			return null;
		LanguageSettings languageSettings = null;
		try {
			languageSettings = (LanguageSettings) SettingsReader.getInstance().readSettings(
					languageFile, LanguageSettings.class);
		} catch (JAXBException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}

		return languageSettings;
	}

	public static void main(String[] args) {
		LModeller lMod = new LModeller();
		GLApp.window_title = "LModelling";
		
		lMod.run();

	}
	
	public static GameSettings getGameSetting()
	{
		return gameSettings;
	}

	
    /**
     * Initialize the scene.  Called by GLApp.run()
     */
    public void setup()
    {
		System.out.println("LModelling.setup(): entering... ");
		long enterTime = System.currentTimeMillis();

    	
    	
        // enable lighting and texture rendering
        GL11.glEnable(GL11.GL_TEXTURE_2D);
        GL11.glEnable(GL11.GL_COLOR_MATERIAL);
        GL11.glEnable(GL11.GL_NORMALIZE);
        GL11.glDisable(GL11.GL_LIGHTING);
        GL11.glClearColor(0.01f,0.01f,0.01f,1f);

        
        // setup and enable perspective
        setPerspective();

        // Create a light
        setLight( GL11.GL_LIGHT1,
        		new float[] { 1f,  1f,  1f,  1f },    // diffuse color
        		new float[] { .2f, .2f, .2f, 1f },    // ambient
        		new float[] { .5f,  .5f,  .5f,  1f },    // specular
        		lightDirection );                     // direction/position

        userMenu.setup();
        gameMenu.setup();
        buildMenu.setup();
        randomMenu.setup();
        setupMenu.setup();
        modelMenu.setup();
        viewModel.setup();
    	viewModel.setViewMode(VIEWMODES.MANUAL);
    	if(gameSettings!=null)
    		setupMenu.setMusicFolder(gameSettings.getMusicFolder());
		BackgroundMusic.getInstance().setOn(false);
		System.out.println("LModelling.setup(): time " + (System.currentTimeMillis() - enterTime) + " ms");

		final SplashScreen splashScreen = SplashScreen.getSplashScreen();
		if(splashScreen != null)
		{
			splashScreen.close();
		}

		refreshView();
    }
    
    protected void loadSettings()
    {
		gameSettings = null;
		try {
			gameSettings = (GameSettings) SettingsReader.getInstance().readSettings(
					SETTINGFILE, GameSettings.class);
		} catch (JAXBException e) {
			e.printStackTrace();
			return;
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
    }

    /**
     * Render one frame.  Called by GLApp.run().
     */
    public void render() {

        // clear depth buffer and color
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);


        // select model view and reset
        GL11.glMatrixMode(GL11.GL_MODELVIEW);

        // Reset the coordinate system to center of screen
        GL11.glLoadIdentity();


        switch(view)
        {
        case USER_MENU:
        	userMenu.render();
        	break;
        case GAME_MENU:
        	gameMenu.render();
        	break;
        case BUILD_MENU:
        	buildMenu.render();
        	break;
        case RANDOM_MENU:
        	randomMenu.render();
        	break;
        case MODEL_MENU:
        	modelMenu.render();
        	break;
        case VIEW_MODEL:
        	viewModel.render();
        	break;
        case SETUP_MENU:
        	setupMenu.render();
        	break;
        }

        // Place the light.  Light will move with the rest of the scene
        setLightPosition(GL11.GL_LIGHT1, lightDirection);
    }

    /**
     * set the field of view and view depth.
     */
    public static void setPerspective()
    {
        // select projection matrix (controls perspective)
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        // fovy, aspect ratio, zNear, zFar
        GLU.gluPerspective(40f,         // zoom in or out of view
                           aspectRatio, // shape of viewport rectangle (width/height)
                           .1f,         // Min Z: how far from eye position does view start
                           500f);       // max Z: how far from eye position does view extend
        // return to modelview matrix
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
    }



    public void mouseMove(int x, int y) {

        switch(view)
        {
        case USER_MENU:
        	userMenu.mouseMove(x, y);
        	break;
        case GAME_MENU:
        	gameMenu.mouseMove(x, y);
        	break;
        case BUILD_MENU:
        	buildMenu.mouseMove(x, y);
        	break;
        case SETUP_MENU:
        	setupMenu.mouseMove(x, y);
        	break;
        case RANDOM_MENU:
        	randomMenu.mouseMove(x, y);
        	break;
        case MODEL_MENU:
        	modelMenu.mouseMove(x, y);
        	break;
        case VIEW_MODEL:
        	viewModel.mouseMove(x, y);
        	break;
		default:
			break;
        }
    }


    public void mouseDown(int x, int y) {
        switch(view)
        {
        case USER_MENU:
        	userMenu.mouseDown(x, y);
        	break;
        case GAME_MENU:
        	gameMenu.mouseDown(x, y);
        	break;
        case BUILD_MENU:
        	buildMenu.mouseDown(x, y);
        	break;
        case SETUP_MENU:
        	setupMenu.mouseDown(x, y);
        	break;
        case RANDOM_MENU:
        	randomMenu.mouseDown(x, y);
        	break;
        case MODEL_MENU:
        	modelMenu.mouseDown(x, y);
        	break;
        case VIEW_MODEL:
        	viewModel.mouseDown(x, y);
        	break;
		default:
			break;
        }
    }


    public void mouseUp(int x, int y) {
        switch(view)
        {
        case USER_MENU:
        	userMenu.mouseUp(x, y);
        	break;
        case GAME_MENU:
        	gameMenu.mouseUp(x, y);
        	break;
        case BUILD_MENU:
        	buildMenu.mouseUp(x, y);
        	break;
        case SETUP_MENU:
        	setupMenu.mouseUp(x, y);
        	break;
        case RANDOM_MENU:
        	randomMenu.mouseUp(x, y);
        	break;
        case MODEL_MENU:
        	modelMenu.mouseUp(x, y);
        	break;
        case VIEW_MODEL:
        	viewModel.mouseUp(x, y);
        	break;
		default:
			break;
        }
    }



    public void keyDown(int keycode) {
        if ((Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL))
        		&& Keyboard.isKeyDown(Keyboard.KEY_N)) {
            view = VIEWS.GAME_MENU;
        }
        if(view==VIEWS.VIEW_MODEL)
        {
            if (Keyboard.isKeyDown(Keyboard.KEY_F1)) {
            	viewModel.toggleDebugMode();
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_UP) ||
            		Keyboard.isKeyDown(Keyboard.KEY_NUMPAD8)) {
            	viewModel.rotate(-5, 0, 0);
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_DOWN) ||
            		Keyboard.isKeyDown(Keyboard.KEY_NUMPAD2)) {
            	viewModel.rotate(5, 0, 0);
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_LEFT) ||
            		Keyboard.isKeyDown(Keyboard.KEY_NUMPAD4)) {
            	viewModel.rotate(0, -5, 0);
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT) ||
            		Keyboard.isKeyDown(Keyboard.KEY_NUMPAD6)) {
            	viewModel.rotate(0, 5, 0);
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_HOME) ||
            		Keyboard.isKeyDown(Keyboard.KEY_NUMPAD7)) {
            	viewModel.rotate(0, 0, -5);
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_END) ||
            		Keyboard.isKeyDown(Keyboard.KEY_NUMPAD1)) {
            	viewModel.rotate(0, 0, 5);
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_ADD) ||
            		Keyboard.isKeyDown(Keyboard.KEY_EQUALS)) {
            	viewModel.zoomIn();
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_MINUS) ||
            		Keyboard.isKeyDown(Keyboard.KEY_SUBTRACT)) {
            	viewModel.zoomOut();
            }

            if (Keyboard.isKeyDown(Keyboard.KEY_R) &&
            		Keyboard.isKeyDown(Keyboard.KEY_UP)) {
            	testColor[0] = testColor[0] + 0.05f;
            	if(testColor[0]>1.0f)
            		testColor[0] = 1.0f;
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_R) &&
            		Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
            	testColor[0] = testColor[0] - 0.05f;
            	if(testColor[0]<0.0f)
            		testColor[0] = 0.0f;
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_G) &&
            		Keyboard.isKeyDown(Keyboard.KEY_UP)) {
            	testColor[1] = testColor[1] + 0.05f;
            	if(testColor[1]>1.0f)
            		testColor[1] = 1.0f;
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_G) &&
            		Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
            	testColor[1] = testColor[1] - 0.05f;
            	if(testColor[1]<0.0f)
            		testColor[1] = 0.0f;
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_B) &&
            		Keyboard.isKeyDown(Keyboard.KEY_UP)) {
            	testColor[2] = testColor[2] + 0.05f;
            	if(testColor[2]>1.0f)
            		testColor[2] = 1.0f;
            }
            if (Keyboard.isKeyDown(Keyboard.KEY_B) &&
            		Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
            	testColor[2] = testColor[2] - 0.05f;
            	if(testColor[2]<0.0f)
            		testColor[2] = 0.0f;
            }
        }
    }


    public void keyUp(int keycode) {
    }

	@Override
	public void selectionEventOccurred(EventObject e) {
		if(e instanceof GameModeSelectionEvent)
		{
			switch(((GameModeSelectionEvent) e).getMode())
			{
			//CANCEL, RANDOM_GAME, RANDOM_STEP_GAME, MEMORY_GAME, MODEL_GAME, BUILD_MENU
			case USER_MENU:
				BackgroundMusic.getInstance().setOn(false);
				view = VIEWS.USER_MENU;
				break;
			case SETUP:
				randomMenu.setMode(MODES.SETUP);
				viewHistory.add(VIEWS.SETUP_MENU);
	            view = VIEWS.SETUP_MENU;
				break;
			case RANDOM_STEP_GAME:
				randomMenu.setMode(MODES.RANDOM_STEP_GAME);
				viewHistory.add(VIEWS.RANDOM_MENU);
	            view = VIEWS.RANDOM_MENU;
				break;
			case MEMORY_GAME:
				randomMenu.setMode(MODES.MEMORY_GAME);
				viewHistory.add(VIEWS.RANDOM_MENU);
	            view = VIEWS.RANDOM_MENU;
				break;
			case MODEL_GAME:
				viewHistory.add(VIEWS.MODEL_MENU);
	            view = VIEWS.MODEL_MENU;
				break;
			case BUILD_MENU:
				viewHistory.add(VIEWS.BUILD_MENU);
	            view = VIEWS.BUILD_MENU;
				break;
			case PREVIOUS:
				if(viewHistory.size()>1)
				{
					viewHistory.remove(viewHistory.size()-1);
					view = viewHistory.get(viewHistory.size()-1);
				}
				else
				{
					viewHistory.clear();
					BackgroundMusic.getInstance().setOn(false);
					view = VIEWS.USER_MENU;
				}
				break;
			case HOME:
				viewHistory.clear();
				viewHistory.add(VIEWS.GAME_MENU);
				view = VIEWS.GAME_MENU;
				break;
			case EXIT:
				GLApp.finished = true;
				break;
			}
		}
		else if(e instanceof ModelSelectionEvent)
		{
			if(((ModelSelectionEvent) e).getPages() == null)
			{
				viewHistory.clear();
				viewHistory.add(VIEWS.GAME_MENU);
				view = VIEWS.GAME_MENU;
			}
			else
			{
				viewModel.setPages(((ModelSelectionEvent) e).getPages());
				viewHistory.add(VIEWS.VIEW_MODEL);
				view = VIEWS.VIEW_MODEL;
			}
		}
		else if(e instanceof UserSelectionEvent)
		{
			gameMenu.setUser(((UserSelectionEvent) e).getUser(), ((UserSelectionEvent) e).getUserTexture());
			buildMenu.setUser(((UserSelectionEvent) e).getUser(), ((UserSelectionEvent) e).getUserTexture());
			setupMenu.setUser(((UserSelectionEvent) e).getUser());
			Logger.getInstance().setUserName(((UserSelectionEvent) e).getUser().getName());
			viewHistory.clear();
			viewHistory.add(VIEWS.GAME_MENU);
			view = VIEWS.GAME_MENU;
		}
		refreshView();
		
	}
	
	protected void refreshView()
	{
		switch(view)
		{
		//USER_MENU, GAME_MENU, RANDOM_MENU, MODEL_MENU, VIEW_MODEL, SETUP_MENU, BUILD_MENU
		case USER_MENU:
			userMenu.viewOpened();
			break;
		case SETUP_MENU:
			setupMenu.viewOpened();
			break;
		case RANDOM_MENU:
			randomMenu.viewOpened();
			break;
		case MODEL_MENU:
			modelMenu.viewOpened();
			break;
		case BUILD_MENU:
			buildMenu.viewOpened();
			break;
		case VIEW_MODEL:
			viewModel.viewOpened();
			break;
		case GAME_MENU:
			gameMenu.viewOpened();
			break;
		}
	}

}
